package Shared;

/**
 * Common structure used for Ambulance in the real and simulation world.
 * 
 */

public abstract class Ambulance {

	private final int id;
	private final AmbType type;

	private AmbState state;
	private MapPosition position;

	/**
	 * Creates a software ambulance.
	 * 
	 * @param id
	 *            the unique ID of the ambulance
	 * @param type
	 *            AmbulanceInfo.EQUIPED or AmbulanceInfo.SIMPLE
	 * @param position
	 *            initial ambulance position
	 */
	public Ambulance(int id, AmbType type, MapPosition position) {
		this.id = id;

		this.type = type;
		this.state = AmbState.UNREGISTERED;
		this.position = position;

	}

	/* *********** */
	/* * SETTERS * */
	/* *********** */

	/**
	 * Set this ambulance's status as logged in : a crew is inside and ready for
	 * assignment.
	 * 
	 * This action is performed on the real ambulance.
	 */
	public void logIn() {
		assert state == AmbState.INACTIVE : "Someone already logged.";
		state = AmbState.WAIT_FOR_MISSION;
	}

	/**
	 * Set this ambulance's status inactive : nobody will use it.
	 * 
	 * This action is performed on the real ambulance.
	 */
	public void logOut() {
		assert state != AmbState.INACTIVE && state != AmbState.UNREGISTERED;

		state = AmbState.INACTIVE;
	}

	/**
	 * Indicates that an ambulance just crashed. It goes into the Error state.
	 */
	public void crash() {
		state = AmbState.ERROR;
	}

	/**
	 * PRE : the crew was previously busy.<br />
	 * 
	 * Set this ambulance's status as free. This is performed when a crew is
	 * again ready for assignment.
	 */
	public void setFree() {
		assert state == AmbState.BUSY;

		state = AmbState.WAIT_FOR_MISSION;
	}

	/**
	 * A notification for assignment has been sent to the real ambulance, but it
	 * has not yet acknowledged it. The ambulance is said to be dispatched.
	 * 
	 * This action is performed by the dispatching soft. (only)
	 */
	public void assign() {
		assert state == AmbState.WAIT_FOR_MISSION : "Cannot assign an ambulance already assigned";
		this.state = AmbState.ASSIGNED;
	}

	/**
	 * The ambulance has accepted the mission and is now active.
	 * 
	 * This action is performed on the real ambulance only
	 */
	public void acceptMission() {
		assert state == AmbState.ASSIGNED : "Cannot accept mision 'none'";
		this.state = AmbState.ON_ITS_WAY;
	}

	/**
	 * The ambulance has finished its mission. The ambulance is now busy and
	 * handling the event.
	 * 
	 * This action is performed on the real ambulance only
	 */
	public void setMissionFinished() {
		assert state.isOnMission() : "Cannot finish a mission when none started";
		state = AmbState.BUSY;
	}

	/**
	 * Logs out the ambulance without setting the "Mission Finished" flag.
	 * 
	 * This action is performed on the real ambulance only
	 */
	public void setMissionFail() {
		assert state.isOnMission() : "Cannot abort a mission when none started";
		this.state = AmbState.ERROR;
	}

	/**
	 * Changes the ambulance position to "position"
	 * 
	 * @param position
	 *            The new position
	 */
	public void setPosition(MapPosition position) {
		this.position = position;
	}

	/**
	 * PRE : ambulance must be unregistered.<br />
	 * 
	 * Changes the status of the ambulance from unregistered to inactive.
	 */
	public void register() {
		assert state == AmbState.UNREGISTERED;

		state = AmbState.INACTIVE;
	}

	/**
	 * PRE : ambulance cannot be on a mission.<br />
	 * 
	 * Unregisters an ambulance
	 */
	public void unregister() {
		assert !isOnMission();

		state = AmbState.UNREGISTERED;
	}

	/**
	 * 
	 * @return true if the ambulance is in the Error state.
	 */
	public boolean isInErrorState() {
		return state == AmbState.ERROR;
	}

	/**
	 * PRE : the ambulance has to be in the Error state.<br />
	 * 
	 * Sets the ambulance as unregistered.
	 */
	public void clearError() {
		assert state == AmbState.ERROR;

		state = AmbState.UNREGISTERED;
	}

	/**
	 * PRE : the ambulance has to be free or on an emergency location.<br />
	 * 
	 * Sets the ambulance state as Busy.
	 */
	public void setBusy() {
		assert state == AmbState.WAIT_FOR_MISSION || state == AmbState.ON_PLACE;

		state = AmbState.BUSY;

	}

	/* *********** */
	/* * GETTERS * */
	/* *********** */

	/**
	 * Checks if the ambulance is available for assignment.
	 * 
	 * @return true if the ambulance is available for assignment.
	 */
	public boolean isFree() {
		return this.state == AmbState.WAIT_FOR_MISSION;
	}

	/**
	 * Checks if the ambulance is equipped
	 * 
	 * @return true if ambulance is equipped
	 */
	public boolean isEquiped() {
		return this.type == AmbType.EQUIPED;
	}

	/**
	 * @return the ambulance's registered position
	 */
	public MapPosition getPosition() {
		return this.position;
	}

	/**
	 * @return the ambulance's unique ID
	 */
	public int getID() {
		return id;
	}

	/**
	 * 
	 * @return the ambulance type
	 */
	public AmbType getType() {
		return type;
	}

	/**
	 * @return true if the ambulance is currently handling a mission.
	 */
	public boolean isOnMission() {
		return state.isOnMission();
	}

	/**
	 * 
	 * @return true if the ambulance is logged (not inactive and not
	 *         unregistered and not in error state).
	 */
	public boolean isLogged() {
		return !(state == AmbState.INACTIVE || state == AmbState.UNREGISTERED || state == AmbState.ERROR);
	}

	/**
	 * 
	 * @return true if the ambulance is not unregistered.
	 */
	public boolean isRegistered() {
		return state != AmbState.UNREGISTERED;
	}

	/**
	 * 
	 * @return the ambulance state.
	 */
	public AmbState getStatus() {
		return state;
	}

	/**
	 * 
	 * @return true if the ambulance is busy.
	 */
	public boolean isBusy() {
		return state == AmbState.BUSY;
	}

}
